package extensions;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import extensions.packet.ClientPacket;
import extensions.packet.PacketBuilder;
import extensions.romario.packet.AbstractPacket;
import extensions.romario.server.MainJME;

public class NetworkClient
{

    private static final int RECEIVE_THREAD_SLEEP = 50;// ms
    private final MainJME _application;
    private final Socket _socket;
    private ObjectOutputStream _out;
    private ObjectInputStream _in;
    private final Server _server;
    private Thread _receiveThread;
    private boolean _runReceiveThread = true;
    private int _id;

    public NetworkClient(int id, Socket socket, Server server,
	    MainJME application) throws IOException
    {
	_socket = socket;
	_server = server;
	_id = id;
	_application = application;

	makeStreams();

	startReceiveThread();
    }

    public String getHostAddress()
    {
	return _socket.getInetAddress().getHostAddress();
    }

    public int getPort()
    {
	return _socket.getPort();
    }

    protected final void makeStreams() throws IOException
    {
	assert (_socket != null);

	_out = new ObjectOutputStream(_socket.getOutputStream());
	_in = new ObjectInputStream(_socket.getInputStream());
    }

    public void sendPacket(AbstractPacket packet) throws IOException
    {
	if (_out == null)
	{
	    throw new NullPointerException("Client output stream is null");
	}

	packet.setOutputStream(_out);
	packet.send();
    }

    public Socket getSocket()
    {
	return _socket;
    }

    protected final void startReceiveThread()
    {
	assert (_in != null);

	if (_receiveThread != null
		&& _receiveThread.getState() != Thread.State.TERMINATED)
	{
	    throw new IllegalStateException(
		    "Receive thread of client is already started.");
	}

	_receiveThread = new Thread(new Runnable()
	{

	    @Override
	    public void run()
	    {
		try
		{
		    _runReceiveThread = true;
		    while (_runReceiveThread)
		    {
			ClientPacket packet;
			int packetId = _in.readShort();

			packet = PacketBuilder.getPacket(packetId);
			packet.setInputStream(_in);
			packet.setClient(NetworkClient.this);
			packet.setApplication(_application);
			packet.setServer(_server);

			try
			{
			    packet.execute();
			} catch (IOException e)
			{
			    e.printStackTrace();
			}

			_server.onPacketReceived(packet, NetworkClient.this);

			Thread.sleep(RECEIVE_THREAD_SLEEP);
		    }
		} catch (InterruptedException e)
		{
		    e.printStackTrace();
		} catch (IOException ex)
		{
		    // ex.printStackTrace();
		}
		_server.onClientDisconnected(NetworkClient.this);
	    }
	});

	_receiveThread.setName("NetworkClient Receive port " + getPort());
	_receiveThread.start();
    }

    public void close()
    {
	_runReceiveThread = false;
    }

    public int getId()
    {
	return _id;
    }
}
